/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// nx::begin/end
#include "algorithm/foreach.h"
// nx_min
#include "utility/tools.h"
// nx_is_container
#include "type/typedetect.h"
// nx_traits
#include "type/typetraits.h"

// std algorithms
#include <algorithm>

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    Swap two variables
*/

template <typename T, typename U>
inline void swap(T& x, U& y)
{
    T t = x;
    x = static_cast<T>(y);
    y = static_cast<U>(t);
}

template <typename T, typename U, size_t N>
inline void swap(T(&x)[N], U(&y)[N])
{
    nx_foreach(i, N)
        nx::swap(x[i], y[i]);
}

/*
    Copy container/variable
*/

template <typename R, typename Iter_>
inline R copy(R r, Iter_ first, Iter_ last)
{
    return std::copy(first, last, r);
}

namespace private_
{
    template <typename T, typename U, 
              bool Check_ = (nx_is_container(T) && nx_is_container(U))>
    struct copy_;

    template <typename T, typename U>
    struct copy_<T, U, true>
    {
        static T& _(T& dst, U& src)
        {
            nx::copy(nx::begin(dst), nx::begin(src), nx::end(src));
            return dst;
        }
    };

    template <typename T, typename U>
    struct copy_<T, U, false>
    {
        static T& _(T& dst, U& src)
        {
            return dst = src;
        }
    };
}

template <typename T, typename U>
inline T& copy(T& dst, U& src)
{
    return private_::copy_<T, U>::_(dst, src);
}

/*
    Fill range
*/

template <typename Iter_, typename V>
inline void fill(Iter_ first, Iter_ last, const V& v)
{
    std::fill(first, last, v);
}

namespace private_
{
    template <typename T, typename V, bool Check_ = nx_is_container(T)>
    struct fill_;

    template <typename T, typename V>
    struct fill_<T, V, true>
    {
        static void _(T& dst, const V& v)
        {
            nx::fill(nx::begin(dst), nx::end(dst), v);
        }
    };

    template <typename T, typename V>
    struct fill_<T, V, false>
    {
        static void _(T& dst, const V& v)
        {
            dst = static_cast<T>(v);
        }
    };
}

template <typename T, typename V>
inline void fill(T& dst, const V& v)
{
    private_::fill_<T, V>::_(dst, v);
}

template <typename T, typename S, typename V>
inline T fill_n(T first, S n, const V& v)
{
    return std::fill_n(first, n, v);
}

/*
    Sort
*/

template <typename Iter_>
inline void sort(Iter_ first, Iter_ last)
{
    std::sort(first, last);
}

template <typename Iter_, typename Comp_>
inline void sort(Iter_ first, Iter_ last, Comp_ func)
{
    std::sort(first, last, func);
}

template <typename T>
inline void sort(T& dst)
{
    nx::sort(nx::begin(dst), nx::end(dst));
}

template <typename T, typename Comp_>
inline void sort(T& dst, Comp_ func)
{
    nx::sort(nx::begin(dst), nx::end(dst), func);
}

/*
    Compare
*/

template <typename Iter1_, typename Iter2_>
inline bool equal(Iter1_ first1, Iter1_ last1, Iter2_ first2)
{
    return std::equal(first1, last1, first2);
}

template <typename Iter1_, typename Iter2_, typename Pred_>
inline bool equal(Iter1_ first1, Iter1_ last1, Iter2_ first2, Pred_ func)
{
    return std::equal(first1, last1, first2, func);
}

namespace private_
{
    template <typename T, typename U, 
              bool Check_ = (nx_is_container(T) && nx_is_container(U))>
    struct equal_;

    template <typename T, typename U>
    struct equal_<T, U, true>
    {
        static bool _(T& x, U& y)
        {
            return nx::equal(nx::begin(x), nx::end(x), nx::begin(y));
        }
    };

    template <typename T, typename U>
    struct equal_<T, U, false>
    {
        static bool _(T& x, U& y)
        {
            return nx::is_equal(x, y);
        }
    };
}

template <typename T, typename U>
inline bool equal(T& x, U& y)
{
    return private_::equal_<T, U>::_(x, y);
}

template <typename Iter1_, typename Iter2_>
inline bool compare(Iter1_ first1, Iter1_ last1, Iter2_ first2, Iter2_ last2)
{
    return std::lexicographical_compare(first1, last1, first2, last2);
}

template <typename Iter1_, typename Iter2_, typename Comp_>
inline bool compare(Iter1_ first1, Iter1_ last1, Iter2_ first2, Iter2_ last2, Comp_ func)
{
    return std::lexicographical_compare(first1, last1, first2, last2, func);
}

template <typename T, typename U>
inline bool compare(T& x, U& y)
{
    return nx::compare(nx::begin(x), nx::end(x), nx::begin(y), nx::end(y));
}

template <typename T, typename U, typename Comp_>
inline bool compare(T& x, U& y, Comp_ func)
{
    return nx::compare(nx::begin(x), nx::end(x), nx::begin(y), nx::end(y), func);
}

/*
    Find
*/

template <typename Iter_, typename V>
inline Iter_ find(Iter_ first, Iter_ last, const V& v)
{
    return std::find(first, last, v);
}

template <typename T, typename V>
inline typename TraitsContainer<T>::ite_t find(T& x, const V& v)
{
    return nx::find(nx::begin(x), nx::end(x), v);
}

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
